﻿using Newtonsoft.Json;
using SignalRHub.Redis.Interface;
using StackExchange.Redis;

namespace SignalRHub.Redis
{
    /// <summary>
    /// 
    /// </summary>
    public class RedisCacheManager : ICacheManager
    {
        private readonly IRedisConnectionWrapper _connectionWrapper;

        private readonly IDatabase _db;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="connectionWrapper"></param>
        public RedisCacheManager(IRedisConnectionWrapper connectionWrapper)
        {
            _connectionWrapper = connectionWrapper;
            _db = _connectionWrapper.GetDatabase();
        }

        /// <summary>
        /// 清理全部
        /// </summary>
        public void Clear()
        {
            foreach (var endPoint in _connectionWrapper.GetEndPoints())
            {
                var server = _connectionWrapper.GetServer(endPoint);
                var keys = server.Keys(_db.Database);
                _db.KeyDelete(keys.ToArray());
            }
        }

        public long StringIncrement(string key, long value)
        {
            long result = _db.StringIncrement(key, value);
            return result;
        }

        /// <summary>
        /// 获取缓存
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public T Get<T>(string key)
        {
           var serializedItem = _db.StringGet(key);

            if (!serializedItem.HasValue)
                return default(T);
            var item = JsonConvert.DeserializeObject<T>(serializedItem);
            if (item == null)
                return default(T);
            return item;
        }

        /// <summary>
        /// 检查缓存是否存在
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool IsSet(string key)
        {
            return _db.KeyExists(key);
        }

        /// <summary>
        /// 移除缓存
        /// </summary>
        /// <param name="key"></param>
        public void Remove(string key)
        {
            _db.KeyDelete(key);
        }

        /// <summary>
        /// 匹配删除多项
        /// </summary>
        /// <param name="pattern"></param>
        public void RemoveByPattern(string pattern)
        {
            foreach(var endPoint in _connectionWrapper.GetEndPoints())
            {
                var server = _connectionWrapper.GetServer(endPoint);
                var keys = server.Keys(_db.Database, $"*{pattern}*");
                _db.KeyDelete(keys.ToArray());
            }
        }

        /// <summary>
        /// 设置缓存
        /// </summary>
        /// <param name="key"></param>
        /// <param name="data"></param>
        /// <param name="cacheTime"></param>
        public void Set(string key, object data, int cacheTime)
        {
            if (data == null)
                return ;

            var expiresIn = TimeSpan.FromMinutes(cacheTime);

             _db.StringSet(key, data.ToString(), expiresIn);
        }

        public async void SetAsync(string key, object data, int cacheTime)
        {
            if (data == null)
                return;

            var expiresIn = TimeSpan.FromMinutes(cacheTime);

           await _db.StringSetAsync(key, data.ToString(), expiresIn);
        }

        /// <summary>
        /// 设置缓存
        /// </summary>
        /// <param name="key"></param>
        /// <param name="data"></param>
        /// <param name="cacheTime"></param>
        public bool SetValue<T>(string key, T data, int cacheTime)
        {
            if (data == null)
                return false;

            var expiresIn = TimeSpan.FromMinutes(cacheTime);

            return _db.StringSet(key, JsonConvert.SerializeObject(data), expiresIn);
        }

        public async Task<bool> SetValueAsync<T>(string key, T data, int cacheTime)
        {
            if (data == null)
                return false;

            var expiresIn = TimeSpan.FromMinutes(cacheTime);

            return await _db.StringSetAsync(key, JsonConvert.SerializeObject(data), expiresIn);
        }

        /// <summary>
        /// 插入List
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void ListSet<T>(string key,List<T> value)
        {
            foreach (var single in value)
            {
                var s = JsonConvert.SerializeObject(single); 
                _db.ListRightPush(key, s); //要一个个的插入
            }
        }

        /// <summary>
        /// 插入ListAsync
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public async Task ListSetAsync<T>(string key, List<T> value)
        {
            foreach (var single in value)
            {
                var s = JsonConvert.SerializeObject(single);
                await _db.ListRightPushAsync(key, s); //要一个个的插入
            }
        }

        /// <summary>
        /// 获取List
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public List<T> ListGet<T>(string key)
        {
            if (!IsSet(key)) return new List<T>();
            //需要逐个反序列化成实体
            var vList = _db.ListRange(key);
            List<T> result = new List<T>();
            foreach (var item in vList)
            {
                var model = JsonConvert.DeserializeObject<T>(item); //反序列化
                result.Add(model);
            }
            return result;
        }
        /// <summary>
        /// 删除List指定值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public long LisRemove<T>(string key, T value)
        {
            return _db.ListRemove(key, JsonConvert.SerializeObject(value));
        }

        /// <summary>
        /// 删除List指定值Async
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public async Task<long> LisRemoveAsync<T>(string key, T value)
        {
            return await _db.ListRemoveAsync(key, JsonConvert.SerializeObject(value));
        }

        /// <summary>
        /// 加锁执行方法（无返回值）
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="lockKey">锁key</param>
        /// <param name="act">获取锁成功后执行的委托</param>
        /// <param name="expiry">锁到期时间，超时自动解锁</param>
        /// <param name="waitTime">最大等待时间（空值代表不等待）</param>
        /// <returns>执行结果</returns>
        public bool ExecuteWithLock(string lockKey, Action act, TimeSpan? expiry = null, TimeSpan? waitTime = null)
        {
            DateTime start = DateTime.Now;
            RedisValue token = Environment.MachineName;

            // 超时时间默认10秒
            TimeSpan expiryTime = TimeSpan.FromSeconds(10);
            if (expiry != null)
            {
                expiryTime = (TimeSpan)expiry;
            }

            bool result;
            while (!(result = _db.LockTake(lockKey, token, expiryTime)))
            {
                if (waitTime == null || DateTime.Now - start > waitTime.Value)
                {
                    break;
                }

                Thread.Sleep(50);
            }

            if (result)
            {
                try
                {
                    act();
                }
                finally
                {
                    _db.LockRelease(lockKey, token);
                }
            }

            return result;
        }
    }
}
